View Javadoc

1   /*
2    * Copyright 2004-2005, University Health Network.  All rights reserved. Distributed under the BSD 
3    * license (see http://opensource.org/licenses/bsd-license.php).
4    *  
5    * Created on 6-Dec-2004
6    */
7   package ca.uhn.cache.impl;
8   
9   import java.util.Iterator;
10  
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.springframework.beans.factory.InitializingBean;
14  
15  import ca.uhn.cache.IDataItem;
16  import ca.uhn.cache.IDataSource;
17  import ca.uhn.cache.IQuery;
18  import ca.uhn.cache.IQueryResult;
19  import ca.uhn.cache.ISemanticCache;
20  import ca.uhn.cache.exception.CacheException;
21  import ca.uhn.cache.exception.DataSourceException;
22  import ca.uhn.cache.util.QueryResultUtil;
23  
24  /***
25   * An <code>IDataSource</code> that is an underlying <code>IDataSource</code>
26   * wrapped in a read-through cache.
27   * 
28   * TODO: We allow a single result to correspond to multiple queries in
29   * IDataSource ... this might create a cache consistency problem ... if a query
30   * is performed, then we get remainder results, then the cached portion is
31   * purged, then we populate the cache with the remainder, then the descriptor of
32   * the remainder will include the cached portion which has been purged. We don't
33   * get this problem if we only allow one query per result.
34   * 
35   * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp </a>
36   * @version $Revision: 1.1 $ updated on $Date: 2005/01/24 22:52:33 $ by $Author:
37   *          btripp $
38   */
39  public class SelfCachingDataSource implements IDataSource, InitializingBean {
40  
41      private static final Log ourLog = LogFactory.getLog( SelfCachingDataSource.class );
42      
43      private static final IQueryResult EMPTY_QUERY_RESULT = new QueryResult();
44  
45      private ISemanticCache mySemanticCache;
46  
47      private IDataSource myDataSource;
48  
49      private int myMaxQueryGroups = -1;
50  
51      //private Executor myExecutor;
52  
53      // /***
54      // * @param theCache cache that holds query results and may partially or completely answer queries
55      // * @param theSource original source of data to be cached / returned to caller as needed
56      // * @param theMaxRemainderQueries maximum number of remainder queries that can be used to obtain 
57      // * the non-cached portion of a query result
58      // */
59      // public SelfCachingDataSource(ISemanticCache theCache, IDataSource
60      // theSource, int theMaxRemainderQueries) {
61      // myCache = theCache;
62      // mySource = theSource;
63      // myMaxQueryGroups = theMaxRemainderQueries;
64      //        
65      // myExecutor = new PooledExecutor(new BoundedBuffer(20), 100);
66      // ((PooledExecutor) myExecutor).setMinimumPoolSize(5);
67      // }
68  
69      /***
70       * Default constructor.
71       */
72      public SelfCachingDataSource() {
73  //        myExecutor = new PooledExecutor( new BoundedBuffer( 20 ), 100 );
74  //        ((PooledExecutor) myExecutor).setMinimumPoolSize( 5 );
75      }
76  
77      /***
78       * Note that there is no optimization for sending multiple queries at once
79       * (queries are executed in series).
80       * 
81       * @see ca.uhn.cache.IDataSource#execute(ca.uhn.cache.IQuery[])
82       */
83      public IQueryResult execute( IQuery[] theQueries ) throws DataSourceException {
84          if (theQueries.length == 0) {
85              throw new IllegalArgumentException( "The list of queries is empty" );
86          }
87  
88          IQueryResult result = execute( theQueries[0] );
89  
90          // merge additional queries ...
91          for (int i = 1; i < theQueries.length; i++) {
92              Iterator nextResults = execute( theQueries[i] ).iterator();
93              while (nextResults.hasNext()) {
94                  result.add( (IDataItem) nextResults.next() );
95              }
96          }
97  
98          return result;
99      }
100 
101     private IQueryResult execute( IQuery theQuery ) throws DataSourceException {
102         //TODO enhance the IQueryResult interface to keep track of any exceptions that were thrown during 
103         //the execution of the "execute" method.
104         
105         IQueryResult retVal;
106         
107         boolean useCachedResult = true;
108 
109         IQueryResult cachedResult = null;
110         try {
111             cachedResult = getSemanticCache().get( theQuery );
112         }
113         catch (CacheException e) {
114             useCachedResult = false;
115             ourLog.error( "Exception thrown while executing the get method of the cache", e );
116         }
117         
118         IQuery[] remainders = null;
119         if ( useCachedResult == true ) {
120             try {
121                 remainders = getSemanticCache().remainder( theQuery, myMaxQueryGroups );
122             }
123             catch (CacheException e) {
124                 useCachedResult = false;
125                 ourLog.error( "Exception thrown while executing the remainder method of the cache", e );
126             }
127         }
128         
129         IQueryResult sourceResult = EMPTY_QUERY_RESULT;
130         if ( useCachedResult == true ) {
131             if ( remainders.length != 0) {
132                 sourceResult = getDataSource().execute( remainders );
133             }
134         }
135         else {
136             sourceResult = getDataSource().execute( new IQuery[] { theQuery } );
137         }
138 
139         if ( useCachedResult == true && !sourceResult.isEmpty() ) {
140             try {
141                 //TODO: scope includes cached data ... synchronization problem?
142                 for (int i = 0; i < remainders.length; i++) {
143                     IQuery remainderQuery = remainders[i];
144                     //the key of the cache must be a remainder query
145                     getSemanticCache().put( remainderQuery, QueryResultUtil.filter( sourceResult, remainderQuery ) );
146                 }
147             }
148             catch (CacheException e) {
149                 ourLog.error( "Exception thrown while executing the put method of the cache", e );
150             } 
151         }
152 
153         IQueryResult filteredSourceResult = QueryResultUtil.filter(sourceResult, theQuery);
154         
155         if ( useCachedResult == true ) {
156             retVal = cachedResult.append( filteredSourceResult );    
157         }
158         else {
159             retVal = filteredSourceResult;
160         }
161             
162         return retVal;
163     }
164 
165 //    private void addResults( final IQuery theQuery, final IDataSource theSource, final QueryProcessor theProcessor )
166 //            throws DataSourceException {
167 //
168 //        Runnable r = new Runnable() {
169 //
170 //            public void run() {
171 //                try {
172 //                    IQueryResult result = theSource.execute( new IQuery[] { theQuery } );
173 //                    theProcessor.setSourceResult( theQuery, result );
174 //                }
175 //                catch (DataSourceException e) {
176 //                    theProcessor.declareException( "Problem querying original source", e );
177 //                }
178 //            }
179 //        };
180 //
181 //        try {
182 //            myExecutor.execute( r );
183 //        }
184 //        catch (InterruptedException e) {
185 //            throw new DataSourceException( e ) {
186 //            };
187 //        }
188 //    }
189 
190     // Do we want these setters & getters? other code shouldn't care, and we
191     // don't want the values to change after instantiation.
192 
193     // ////////SPRING BEGIN /////////
194 
195     /***
196      * @return Semantic cache that holds query results and may partially or completely answer queries.
197      */
198     public ISemanticCache getSemanticCache() {
199         return mySemanticCache;
200     }
201 
202     /***
203      * @param theSemanticCache The semantic cache to set.
204      */
205     public void setSemanticCache( ISemanticCache theSemanticCache ) {
206         mySemanticCache = theSemanticCache;
207     }
208 
209     /***
210      * @return The maximum number of queries that will be sent to the underlying data source at a time.
211      * 
212      * @see ISemanticCache#remainder(IQuery, int)
213      */
214     public int getMaxQueryGroups() {
215         return myMaxQueryGroups;
216     }
217 
218     /***
219      * @param theMaxQueryGroups The maxQueryGroups to set.
220      */
221     public void setMaxQueryGroups( int theMaxQueryGroups ) {
222         myMaxQueryGroups = theMaxQueryGroups;
223     }
224 
225     /***
226      * The underlying data source.
227      * 
228      * @return Returns the source.
229      */
230     public IDataSource getDataSource() {
231         return myDataSource;
232     }
233 
234     /***
235      * @param theDataSource The source to set.
236      */
237     public void setDataSource( IDataSource theDataSource ) {
238         myDataSource = theDataSource;
239     }
240 
241     /***
242      * {@inheritDoc}
243      */
244     public void afterPropertiesSet() throws Exception {
245         assert getDataSource() != null;
246         assert getSemanticCache() != null;
247         assert getMaxQueryGroups() != -1;
248     }
249 
250     // ////////SPRING END /////////
251 
252 }